/**
 * Klasseopmerkingen:
 * 		setThrusterActive
 */

package asteroids.model;


import java.util.ArrayList;
import java.util.*;

import asteroids.Util;
import be.kuleuven.cs.som.annotate.*;

public class Ship extends CollisionObject{
	/**
	 * This new ship will be created with the given specifications in the arguments.
	 * @param xCoordinate				The current x-value of the ships center (2d)
	 * @param yCoordinate				The current y-value of the ships center (2d)
	 * @param xVelocity					The current rate at which the x-value changes
	 * @param yVelocity					The current rate at which the y-value changes
	 * @param radius					The distance between the center of the ship and the outer rim
	 * @param angle						The direction that the ship is pointing at (2d)
	 * @param mass 						The weight of the ship
	 * @throws IllegalRadiusException	If the given radius is smaller than the radiusconstraint, than the exception is thrown
	 * @throws OutOfBoundsException		If one (or both) of the coordinates aren't numbers, than the exception is thrown
	 * @post	If radius ,xVelocity and yVelocity are valid inputs, then there will be made an object of the class ship,
	 * 			with the given values
	 * 			|if(isValidVelocity(xVelocity,yVelocity)==true && isValidRadius(radius) == true && isValidCoordinate(xCoordinate) && isValidCoordinate(yCoordinate)){
	 * 			new.getXCoordinate() == xCoordinate
	 * 			new.getYCoordinate() == yCoordinate
	 * 			new.getXVelocity() == xVelocity
	 * 			new.getYVelocity() == yVelocity
	 * 			new.getRadius() == radius
	 * 			new.getAngle() == angle
	 * @invar	The radius may not exceed the radiusConstraint
	 * 			|isValidRadius(radius) == true
	 * @invar	the velocity may never exceed 300000 km/s
	 * 			|isValidVelocity(xVelocity,yVelocity) == true
	 * @invar	The position has to be in the 2d-field
	 * 			|isValidCoordinate(xCoordinate) == true && isValidCoordinate(yCoordinate) == true
	 * @invar	The mass of a ship has to be larger than 0
	 * 			|isValidMass(mass) == true		
	 */ 
	public Ship (double xCoordinate, double yCoordinate, double xVelocity, double yVelocity, double radius, double angle,double mass) throws OutOfBoundsException,IllegalRadiusException{
		super(xCoordinate, yCoordinate, xVelocity, yVelocity, radius);
		this.angle = angle;
		setMass(mass);
	}
	protected  void setMass(double mass) throws IllegalMassException{
		if(isValidMass(mass))
			this.mass = mass;
		else {throw new IllegalMassException("That mass is not allowed");}
	}


	/**
	 * 
	 * @param a		The duration of the boost
	 * @post	If the new velocity is valid then, the new velocity is the old velocity +the given velocity, scaled with the angle of the ship
	 * 			|if(isValidVelocity(xVelocity+ a*cos(angle),yVelocity + a*sin(angle))){
	 * 			|(new this).getXVelocity() == xVelocity + a*cos(angle)
	 * 			|(new this).getYVelocity() == yVelocity + a*sin(angle)}
	 * @post	If the velocity isn't valid, then the new velocity is equal to the velocityConstraint
	 * 			|if(!isValidVelocity(xVelocity+ a*cos(angle),yVelocity + a*sin(angle))){
	 * 			|velocityToAdd = velocityToAdd(xVelocity(), yVelocity())
	 * 			|(new this).getXVelocity() == xVelocity() + velocityToAdd
	 * 			|(new this).getYVelocity() == yVelocity() + velocityToAdd
	 */
	public void thrust(double dt){
		double amount = checkDuration(dt);
		double newXVelocity = getXVelocity() + amount*Math.cos(getAngle())*getTotalAcceleration();
		double newYVelocity = getYVelocity() + amount*Math.sin(getAngle())*getTotalAcceleration();
		if(isValidVelocity(newXVelocity,newYVelocity) == true){
			//setXVelocity(newXVelocity);
			//setYVelocity (newYVelocity);	
			setValidVelocity(newXVelocity,newYVelocity);

		}
		else{
			double velocityToAdd =velocityToAdd(getXVelocity(), getYVelocity());
			setXVelocity(getXVelocity() + velocityToAdd*Math.cos(getAngle()));
			setYVelocity(getYVelocity() + velocityToAdd*Math.sin(getAngle()));
		}
	}

	private double velocityToAdd(double xVelocity, double yVelocity){
		double totalVelocity = getTotalVelocity(xVelocity, yVelocity);
		double velocityToAdd = getVelocityConstraint() - totalVelocity;
		return velocityToAdd;
	}



	@Basic
	public double getRadiusConstraint(){
		return this.radiusConstraint;
	}
	private final double radiusConstraint = 10;

	@Basic
	public double getAngle(){
		return angle;
	}
	/**
	 * 
	 * @param angle	The direction that the ship is pointing at (2d)
	 * @pre			The angle must be a finite number
	 * 				|!Double.isInfinite(angle);
	 * @pre			The angle must be a valid number
	 * 				|!Double.isNaN(angle)
	 * @post		The given angle is the new angle
	 * 				|(new this).getAngle() == angle
	 */
	public void setAngle(double angle){
		assert !Double.isInfinite(angle);
		assert !Double.isNaN(angle);
		this.angle = angle;
	}

	/**
	 * 
	 * @param angle	The direction that the ship is pointing at (2d)
	 * @post	the new angle is the original angle adding up the given angle
	 * 			|(new this).getAngle() == angle + givenAngle 
	 */
	public void turn(double givenAngle){
		setAngle(getAngle() + givenAngle)	;
	}

	private double angle;


	/**
	 * 
	 * @param duration	The time that a method gets
	 * @return	if the given duration is larger then zero, then result is duration
	 * 			otherwise the duration becomes zero
	 * 			|if(duration>0){result == true}
	 */
	private double checkDuration(double duration){
		if(	Util.fuzzyLessThanOrEqualTo(duration, 0) ==true){return 0;}
		else return duration;
	}


	@Override
	protected double getMinimumMass() {
		return 0;
	}


	@Override
	protected double getMaximumMass() {
		return Double.POSITIVE_INFINITY;
	}
	@Override
	//TODO 3 maal true
	protected boolean isValidMass(double mass){
		if(Util.fuzzyLessThanOrEqualTo(mass, getMinimumMass())){
			return true;
		}
		else if(Util.fuzzyLessThanOrEqualTo(getMaximumMass(), mass)){
			return true;
		}
		else return true;
	}


	@Override
	protected CollisionObject getObjectAtColissionPosition(CollisionObject ship, double timeLapse) {
		double newXCoordinate = ship.getXVelocity()*timeLapse + ship.getXCoordinate();
		double newYCoordinate = ship.getYVelocity()*timeLapse + ship.getYCoordinate();
		Ship resultShip = new Ship(newXCoordinate,newYCoordinate,ship.getXVelocity(),ship.getYVelocity(),ship.getRadius(),((Ship) ship).getAngle(),ship.getMass());
		return resultShip;
	}

	public void setThruster(boolean thruster){
		this.thruster = thruster;
	}


	public boolean getThruster(){
		return this.thruster;
	}



	public boolean isShipThrusterActive(){
		if(this.getThruster() == true)
		{
			return true;
		}
		return false;
	}

	//VERANDERD NAAR EEN METHODE DIE NIET VRAAGT NAAR SHIP, DIT HOORT BIJ SHIP
	/**
	 * Enables or disables <code>ship</code>'s thruster depending on the value of
	 * the parameter <code>active</code>.
	 */
	public void setThrusterActive(boolean active){
		this.setThruster(active);
		if(this.isShipThrusterActive()){
			this.thrust(1);
		}
	}

	public double getThrusterForce() {
		return thrusterForce;
	}

	public double getTotalAcceleration(){
		return getThrusterForce()/getMass();
	}

	public double getXAcceleration(){
		//A force is only excerted in the direction of the angle
		return getTotalAcceleration()*Math.cos(this.getAngle());
	}

	public double getYAcceleration(){
		return getTotalAcceleration()*Math.sin(this.getAngle());
	}

	//public void setThrusterForce(double thrusterForce) {
	//this.thrusterForce = thrusterForce;
	//}
	private double thrusterForce = 1.1*Math.pow(10, 18);

	/**
	 * <code>ship</code> fires a bullet.
	 */
	public void fireBullet(){
		//maak check voor isReadyToFire?
		//TODO die 250 verplaatsen naar een veld initialBulletSpeed
		if(getWorld() != null){
			Bullet bullet = new Bullet(this.getXCoordinate() + Math.cos(this.getAngle())*this.getRadius(),
					this.getYCoordinate() + Math.sin(this.getAngle())*this.getRadius(), 250*Math.cos(this.getAngle()), 250*Math.sin(this.getAngle()), getStandardRadius(), this);
			this.addBullet(bullet);
		}
	}

	private boolean thruster; 
	private double standardRadius = 3;

	public List<Bullet> getBullets(){
		ArrayList <Bullet> bullet = this.bullets;
		return bullet;
	}

	public void addBullet(Bullet bullet){
		this.bullets.add(bullet);
	}
	public void addBulletAtPosition(Bullet bullet,int position)
	{
		this.bullets.add(position,bullet);
	}

	public double getStandardRadius() {
		return standardRadius;
	}


	//public void setStandardRadius(double standardRadius) {
	//this.standardRadius = standardRadius;
	//}
	private ArrayList <Bullet> bullets;

	@Override
	public void collisionReactionWith(CollisionObject collisionObject) {
		if(collisionObject.isShip()){
			this.bounceOff();
			collisionObject.bounceOff();
		}
		if(collisionObject.isBullet()){
			collisionObject.destroy();
			this.destroy();
		}
		if(collisionObject.isAsteroid()){
			this.destroy();
		}
	}







}


